<?php
/**
 * @package mikrokosmos
 * mk structures file created on 13-gen-2005
 * jaco_at_pixeldump_dot_org
 */

///////////////////////////////////////////////////////////////////////////////
// MAIN DRAWING CLASS
// MKShapeContainer should be the only public class
// STRUCTURES and SHAPE RECORDS are intended to be used by mk framework
///////////////////////////////////////////////////////////////////////////////

if(!class_exists("MKTagHeader")){ class MKTagHeader {}}


/**
 * the main shape manager
 * it is a wrapper for DefineShape[2|3] tag
 * contains primitives for drawing (see methods)
 * and a "placing" object so it can be
 * translated, rotated and scaled
 */
class MKShapeContainer extends MKTagHeader {

	var $tagData;
	var $shapeBounds;
	var $edgeBounds;

	var $reserved = 0;			// UB[6] Must be 0.
	var $usesNonScalingStrokes;	// UB[1] If 1, the shape contains at least
								// one non-scaling stroke.
	var $usesScalingStrokes;	// UB[1] If 1, the shape contains at least
								// one scaling stroke.

	var $fsAr = array();		// collection of FILLSTYLEARRAY objects
	var $lsAr = array();		// collection of LINESTYLEARRAY objects
	var $fsbAr = array();		// fillStyleBits array
	var $lsbAr = array();		// lineStyleBits array
	var $srAr = array();		// SHAPERECORD array

	var $x = 0;					// absolute position x in pixel
	var $y = 0; 				// absolute position y in pixel
	var $initialX = 0;
	var $initialY = 0;
	var $xmin = 57600;
	var $xmax = -57600;
	var $ymin = 57600;
	var $ymax = -57600;

	var $currentLFi = 0;		// current left fill index (fillstyle0)
	var $currentRFi = 0;		// current right fill index (fillstyle1)
	var $currentLSi = 0;		// current linestyle index
	var $flagNewStyles = NO_STYLE;	// boolean value used to manage stateNewStyles

	var $shapeDepth = 0;		// a virtual shape Depth, for dealing to stylechange record
								// it will be used as index in fsAr, lsAr, fsbAr, lsbAr

	var $po;					// placeobject2 data

	var $nodeName = "shape";

	// the constructor
	function MKShapeContainer($tagData = "", $itemID = 1, $tagID = SHAPE_ID){

		$this->tagID = $tagID;
		$this->itemID = $itemID;
		if(strlen($tagData)) $this->set_tagData($tagData);
		else {
			$this->shapeBounds = new MKRect("", 57600, -57600, 57600, -57600);

			$this->fsAr[0] = new MKFillStyleArray("", $tagID);
			$this->lsAr[0] = new MKLineStyleArray("", $tagID);
		}

		// create placeObject2
		// suppose to have the same depth and characterID
		// take care to change depth/characterID if you plan
		// to deal with different values
		$this->po = new MKPlaceObject2("", $itemID, $itemID);
	}

	function set_tagData($tagData){
		$this->tagData = $tagData;
		parent::MKTagHeader(substr($tagData, 0, 8));
		$this->get_shape_container_data();
	}
	function get_tagData(){ return $this->tagData; }

	function set_itemID($itemID){
		$this->itemID = $itemID;
		$this->po->set_characterID($itemID);
		$this->po->set_depth($itemID);
	}

	// alias of set_itemID
	function set_shapeID($shapeID) { $this->set_itemID($shapeID); }
	function get_shapeID() { return $this->itemID; }

	function set_shapeBounds($shapeBounds){ $this->shapeBounds = $shapeBounds;}
	function get_shapeBounds(){ return $this->shapeBounds;}

	function set_edgeBounds($edgeBounds){
		$this->tagID = SHAPE_ID4;
		$this->edgeBounds = $edgeBounds;
	}
	function get_edgeBounds(){ return $this->edgeBounds;}

	function set_fillStyleArray($fsAr){ $this->fsAr = $fsAr;}
	function get_fillStyleArray(){ return $this->fsAr;}

	function set_lineStyleArray($lsAr){ $this->lsAr = $lsAr;}
	function get_lineStyleArray(){ return $this->lsAr;}

	function set_fillStyleBitsArray($fsbAr){ $this->fsbAr = $fsbAr;}
	function get_fillStyleBitsArray(){ return $this->fsbAr;}

	function set_lineStyleBitsArray($lsbAr){ $this->lsbAr = $lsbAr;}
	function get_lineStyleBitsArray(){ return $this->lsbAr;}

	function set_shapeRecordArray($srAr){ $this->sr = $srAr;}
	function get_shapeRecordArray(){ return $this->srAr;}

	function set_shapeDepth($shapeDepth){ $this->shapeDepth = $shapeDepth;}
	function get_shapeDepth(){ return $this->shapeDepth;}

	function set_depth($depth){ $this->po->set_depth($depth); }

	function set_placeObject2($po){ $this->po = $po; }
	function get_placeObject2(){ return $this->po; }

	function get_placeDepth(){ return $this->po->get_depth(); }

	function set_replace_flag($rFlag = true) { $this->po->set_pfMove($rFlag);}

	//
	function set_shape_container_data(){
		/* TO DO (maybe) */
	}

	//
	function get_shape_container_data(){

		$bCur = 2; // byte cursor
		$tagBody = substr($this->tagData, $this->tagSL);

		$this->shapeBounds = new MKRect(substr($tagBody, $bCur, MAX_RECT_LENGTH));
		$bCur += $this->shapeBounds->get_length();
		$this->fsAr[0] = new MKFillStyleArray(substr($tagBody, $bCur), $this->tagID);

		$bCur += $this->fsAr[0]->get_length();

		$this->lsAr[0] = new MKLineStyleArray(substr($tagBody, $bCur), $this->tagID);
		$bCur += $this->lsAr[0]->get_length();

		$this->fsbAr[0] = read_fillStyle_nBits($tagBody[$bCur]);
		$this->lsbAr[0] = read_lineStyle_nBits($tagBody[$bCur]);
		$bCur++;

		$readb = substr($tagBody, $bCur);
		$rao = array();			// read-ahead cursor/offset
		$rao["cursor"] = 0;
		$rao["offset"] = 0;

		$k = 0;
		do {

			$fli = count($this->fsAr); 		// fill/line index
			$fsb = $this->fsbAr[$fli - 1];		// select fillStyle bits index
			$lsb = $this->lsbAr[$fli - 1];		// same for lineStyle
			$sri = count($this->srAr);
			$srHead = get_binData_string(substr($readb, 0, 2));
			$srHData = substr($srHead, $rao["offset"], 8);
			$sr = parse_shape_record($srHData);

			$srtagData = substr($readb, 0);

			if($sr & IS_STYLECHANGERECORD){

				$this->srAr[$sri] = new MKStyleChangeRecord($srtagData, $fsb, $lsb, $rao["offset"]);

				if($this->srAr[$sri]->get_stateNewStyles() == "1"){ // there are new fill/line style array to set

					$this->fsAr[$fli] = new MKFillStyleArray("", $this->tagID);
					$this->lsAr[$fli] = new MKLineStyleArray("", $this->tagID);

					$this->fsbAr[$fli] = $this->srAr[$sri]->get_nFillBits();
					$this->lsbAr[$fli] = $this->srAr[$sri]->get_nLineBits();

					$fsad = $this->srAr[$sri]->get_fsaData();
					$this->fsAr[$fli]->set_fillStyle_array_data($fsad);

					$lsad = $this->srAr[$sri]->get_lsaData();
					$this->lsAr[$fli]->set_lineStyle_array_data($lsad);

					$readb = substr($readb, ($rao["offset"] + $this->srAr[$sri]->get_bitLength()) / 8);
					$rao["cursor"] = 0;
					$rao["offset"] = 0;
				}
				else $rao = get_readahead_offset($rao["offset"], $this->srAr[$sri]->get_bitLength());
			}
			else if($sr & IS_STRAIGHT_EDGERECORD){
				$this->srAr[$sri] = new MKStraightEdgeRecord($srtagData, $rao["offset"]);
				$rao = get_readahead_offset($rao["offset"], $this->srAr[$sri]->get_bitLength());
			}
			else if($sr & IS_CURVED_EDGERECORD){
				$this->srAr[$sri] = new MKCurvedEdgeRecord($srtagData, $rao["offset"]);
				$rao = get_readahead_offset($rao["offset"], $this->srAr[$sri]->get_bitLength());
			}

			$readb = substr($readb, $rao["cursor"]);
			$rao["cursor"] = 0;

			if($k++ == 500) die("max iteration exceeded");

		} while ($sr != IS_END_SHAPERECORD);
	}

	//
	function increase_shapeDepth($newStyle = false){

		if($this->tagID == SHAPE_ID2) $this->update_tagID(SHAPE_ID2);

		$this->shapeDepth++;
		$sd = $this->shapeDepth;
		$this->lsAr[$sd] = new MKLineStyleArray("", $this->tagID);
		$this->fsAr[$sd] = new MKFillStyleArray("", $this->tagID);
		$this->lsbAr[$sd] = 0;
		$this->fsbAr[$sd] = 0;
		$this->currentLFi = 0;
		$this->currentRFi = 0;
		$this->currentLSi = 0;

		//$this->flagNewStyles =  (int) HAS_NEW_STYLE | (int) ALL_STYLE;
		$this->flagNewStyles = NO_STYLE;

		$sri = count($this->srAr);
		$this->srAr[$sri] = new MKStyleChangeRecord("", $this->tagID);
		$this->srAr[$sri]->set_stateNewStyles((int) HAS_NEW_STYLE | LINE_STYLE | BOTH_FILL);

		$this->clear_fillStyle();
		$this->clear_lineStyle();
	}

	// from absolute to local coord, x and y must be updated
	function get_local_coord($xy, $xory = "x"){

		$tmpCoord = ($xory == "x") ? $this->x : $this->y;

		if($xy < $tmpCoord) $coord = -(abs($tmpCoord + $xy * -1));
		else $coord = $xy + $tmpCoord * -1;

		//if($xy < 0) $coord *= -1;

		if($xory == "x") {
			$this->x = $xy;
		}
		else {
			$this->y = $xy;
		}

		$this->update_shapeBounds();
		return $coord;
	}

	// track shapeBounds
	function update_shapeBounds(){

		//$r = $this->shapeBounds->fetch_assoc(false);

		if($this->xmin > $this->x) $this->xmin = $this->x;
		if($this->xmax < $this->x) $this->xmax = $this->x;
		if($this->ymin > $this->y) $this->ymin = $this->y;
		if($this->ymax < $this->y) $this->ymax = $this->y;

		/*
		if($this->x < $r["xmin"]) $this->shapeBounds->set_xmin($this->x);
		if($this->x > $r["xmax"]) $this->shapeBounds->set_xmax($this->x);
		if($this->y < $r["ymin"]) $this->shapeBounds->set_ymin($this->y);
		if($this->y > $r["ymax"]) $this->shapeBounds->set_ymax($this->y);
		*/

		$this->shapeBounds->set_xmin($this->xmin);
		$this->shapeBounds->set_xmax($this->xmax);
		$this->shapeBounds->set_ymin($this->ymin);
		$this->shapeBounds->set_ymax($this->ymax);
	}

	// update tagID for all object tha support tagID in this class
	function update_tagID($tagID){

		if($this->tagID == $tagID) return;

		$this->tagID = $tagID;

		foreach($this->fsAr as $fs) $fs->update_tagID($tagID);
		foreach($this->lsAr as $ls) $ls->update_tagID($tagID);

		foreach($this->srAr as $sr)
			if(get_class($sr) == "MKStyleChangeRecord")
				$sr->update_tagID($tagID);
	}

	// return the whole DefineShape[2|3] data
	// updating from its members
	function update_tagData(){

		$rawData = swf_word($this->itemID);

		$this->update_shapeBounds();
		$rawData .= $this->shapeBounds->update_buf();

		$this->fsbAr[0] = 0;
		$fsc = $this->fsAr[0]->get_fsCount();
		if($fsc){
			$this->fsbAr[0] = boundBits($fsc);
			$rawData .=  $this->fsAr[0]->update_buf();
		}
		else $rawData .= NIL;

		$this->lsbAr[0] = 0;
		$lsc = $this->lsAr[0]->get_lsCount();
		if($lsc){
			$rawData .=  $this->lsAr[0]->update_buf();
			$this->lsbAr[0] = boundBits($lsc);
		}
		else $rawData .= NIL;

		$flBits = swf_value2bin($this->fsbAr[0], 4);
		$flBits .= swf_value2bin($this->lsbAr[0], 4);
		$rawData .= chr(bindec($flBits));

		$binStr = "";
		$fli = 1;

		foreach($this->srAr as $sr){

			if(get_class($sr) == "MKStyleChangeRecord"){

				$sr->set_fsBits($this->fsbAr[$fli - 1]);
				$sr->set_lsBits($this->lsbAr[$fli - 1]);

				$srBinStr = $sr->get_binStr_before_pad();
				$binStr .= $srBinStr;
				$tmptagData = get_binData($binStr);

				$rawData .= $tmptagData;
				$binStr = substr($binStr, strlen($tmptagData) * 8);
				$sns = $sr->get_stateNewStyles();

				if($sns){

					$rawData .= get_binData($binStr, true);
					$binStr = ""; // data are now byte aligned

					$this->fsbAr[$fli] = 0;
					if(isset($this->fsAr[$fli])){

						$fsc = $this->fsAr[$fli]->get_fsCount();

						if($fsc) $this->fsbAr[$fli] = boundBits($fsc);
						$rawData .=  $this->fsAr[$fli]->update_buf();
					}
					else $rawData .= NIL;

					$this->lsbAr[$fli] = 0;
					if(isset($this->lsAr[$fli])){

						$lsc = $this->lsAr[$fli]->get_lsCount();

						if($lsc) $this->lsbAr[$fli] = boundBits($lsc);
						$rawData .=  $this->lsAr[$fli]->update_buf();
					}
					else $rawData .= NIL;

					$flBits = swf_value2bin($this->fsbAr[$fli], 4);
					$flBits .= swf_value2bin($this->lsbAr[$fli], 4);
					$rawData .= chr(bindec($flBits));

					$fli++;
				}
			}
			else {

				$srBinStr = $sr->update_binStr();
				$binStr .= $srBinStr;
				$tmptagData = get_binData($binStr);
				$rawData .= $tmptagData;
				$binStr = substr($binStr, strlen($tmptagData) * 8);
			}
		}

		$esr = new MKEndShapeRecord();
		$binStr .= $esr->get_binStr();

		$rawData .=  get_binData($binStr, true);

		$this->tagData = SWFTag_long_header($this->tagID, strlen($rawData));
		$this->tagData .= $rawData;

		return $this->tagData;
	}

	// returns placeObject2 tagData
	function update_place_tagData($replacingDepth = -1){
		return $this->po->update_tagData($replacingDepth);
	}

	// add a fillstyle, if new fillstyle is already stored in fsAr it will
	// refresh currentRFi or currentLFi without adding anything
	function add_fillStyle_solid($cAr = array("red" => 0, "green" => 0, "blue" => 0),
								$fSel = RIGHT_FILL){

		if(is_string($cAr)){

			//$alpha = 0xff;

			if(strlen($cAr) > 7){
				$alpha = hexdec(substr($cAr, 7));
				$cAr = parse_color($cAr, $alpha, true);
				$this->update_tagID(SHAPE_ID3);
			}
			else $cAr = get_color_array($cAr);
		}

		if(!count($cAr)){
			if($this->tagID == SHAPE_ID3) $cAr["alpha"] = 0xff;
		}

		$fsi = $this->fsAr[$this->shapeDepth]->add_fillStyle_solid($cAr);
		$this->set_fillFlags($fsi, $fSel);

		if($this->tagID != SHAPE_ID3 && isset($cAr["alpha"])) $this->update_tagID(SHAPE_ID3);

		return $fsi;
	}

	// alias fillStyle_solid
	function add_fill($cAr = array("red" => 0, "green" => 0, "blue" => 0), $fSel = RIGHT_FILL){
		$this->add_fillStyle_solid($cAr, $fSel);
	}

	// unset drawing fill color, it doesn't touch anything in fsAr
	function clear_fillStyle($fSel = NO_STYLE){
		if(!($fSel & LEFT_FILL)) $this->currentLFi = 0;
		if(!($fSel & RIGHT_FILL)) $this->currentRFi = 0;
	}

	// select right or left fill, alternatively
	// useful for select ONLY ONE fillstyle
	// preserve lineStyle flag bit
	function select_fill($fSel = RIGHT_FILL){

		//$fns = $this->flagNewStyles;
		//$lrf = 0;
		//$ls = $fns & LINE_STYLE;

		if($fSel == RIGHT_FILL){

			if(!$this->currentRFi)
				$this->swap_fillStyle();

			$this->currentLFi = 0;
			$ls = $this->flagNewStyles & (int) LINE_STYLE;
			$this->flagNewStyles |= $ls | RIGHT_FILL;

			return;
		}
		else{

			if(!$this->currentLFi) $this->swap_fillStyle();

			$this->currentRFi = 0;
		}

		$this->flagNewStyles |= BOTH_FILL;	// preserve lineStyle flag bit
	}

	//
	function set_fillFlags($fsi, $fSel = RIGHT_FILL){

		if($fSel & LEFT_FILL){
			$this->flagNewStyles = $this->flagNewStyles | LEFT_FILL;
			$this->currentLFi = $fsi;
		}
		else if($fSel & RIGHT_FILL){
			$this->flagNewStyles = $this->flagNewStyles | RIGHT_FILL;
			$this->currentRFi = $fsi;
		}
	}

	//
	function swap_fillStyle(){
		$RFi = $this->currentRFi;
		$this->currentRFi = $this->currentLFi;
		$this->currentLFi = $RFi;
	}

	// unset drawing line color, it don't touch anything in lsAr
	function clear_lineStyle(){
		//$this->flagNewStyles = $this->flagNewStyles & BOTH_FILL;
		$this->currentLSi = 0;
	}

	//
	// gf can be: a MKGradient object o fillstyletype
	// colors, array of colors containing alpha values
	// ratios, ratios array
	// mad, data for gradientmatrix (x, y, width, height, rotation)
	function add_gradient_fill( $gf = LINEAR_GRADIENT_FILL,
								$colors = array(),
								$ratios = array(),
								$mad = array(),
								$drawFromCenter = false,
								$fSel = RIGHT_FILL){

		if(get_class($gf) == "MKFillStyle"){
			$gfObj = $gf;
		}
		else {

			$gfObj = new MKFillStyle();
			$gfObj->set_fillStyleType($gf);

			$gad["colors"] = is_array($colors) ? $colors : array($colors);
			$gad["ratios"] = is_array($ratios) ? $ratios : array($ratios);

			$gfObj->set_gradientMatrix($mad, $drawFromCenter);
			$gfObj->set_gradient($gad);
		}

		$fsi = $this->fsAr[$this->shapeDepth]->add_gradient_fill($gfObj);
		$this->set_fillFlags($fsi, $fSel);

		return $fsi;
	}

	// due to a weird, non documented reason, when adding
	// bitmap_fill containing rotation or scale values
	// different from default it has to be added
	// another bitmap_fill with a 0xffff bitmap id
	// and default matrix
	function add_dummy_bitmap_fill($bitmapType = CLIPPED_BITMAP_FILL, $flagOverrideRFi = false){

		$dbf = new MKFillStyle();
		$dbf->set_fillStyleType($bitmapType);
		$dbf->set_bitmapID(0xffff);
		$mad = array();
		$mad["x"] = 0;
		$mad["y"] = 0;

		$dbf->set_bitmapMatrix($mad);
		$rfi = $this->fsAr[$this->shapeDepth]->add_bitmap_fill($dbf);

		if(!$flagOverrideRFi) $this->currentRFi = $rfi;
	}

	//
	function add_bitmap_fill($bitmapID = 1, $bf = CLIPPED_BITMAP_FILL, $bitmapArgs = array(), $newDepth = false){

		$fSel = RIGHT_FILL;

		$moveX = isset($bitmapArgs["x"]) ? $bitmapArgs["x"] : 0;
		$moveY = isset($bitmapArgs["y"]) ? $bitmapArgs["y"] : 0;
		$rotation = isset($bitmapArgs["rotation"]) ? $bitmapArgs["rotation"] : 0;
		$scaleX = isset($bitmapArgs["scaleX"]) ? $bitmapArgs["scaleX"] / 100 : 1;
		$scaleY = isset($bitmapArgs["scaleY"]) ? $bitmapArgs["scaleY"] / 100 : 1;

		$sd = $this->shapeDepth;

		if($newDepth) {

			$this->update_tagID(max(SHAPE_ID2, $this->tagID));
			$this->shapeDepth++;

			$sd = $this->shapeDepth;

			$this->lsAr[$sd] = new MKLineStyleArray("", $this->tagID);
			$this->fsAr[$sd] = new MKFillStyleArray("", $this->tagID);
			$this->lsbAr[$sd] = 0;
			$this->fsbAr[$sd] = 0;
			$this->currentLFi = 0;
			$this->currentRFi = 0;
			$this->currentLSi = 0;

			//$this->flagNewStyles =  (int) HAS_NEW_STYLE | (int) ALL_STYLE;
			$this->flagNewStyles = NO_STYLE;

			$scr = new MKStyleChangeRecord("", $this->tagID);
			$scr->set_stateNewStyles(ALL_STYLE | HAS_NEW_STYLE);
			$scr->set_stateMoveTo(true);
			$scr->set_moveX(0);
			$scr->set_moveY(0);
			$scr->set_nBitsMoveTo(1);

			$sri = count($this->srAr);
			$this->srAr[$sri] = $scr;

			$this->clear_fillStyle();
			$this->clear_lineStyle();
		}

		if($rotation || $scaleX != 1 || $scaleY != 1 || $moveX || $moveY){
			$this->add_dummy_bitmap_fill($bf, $newDepth);
		}


		$bfObj = new MKFillStyle();
		$bfObj->set_fillStyleType($bf);

		$bfObj->set_bitmapMatrix($bitmapArgs, $bitmapID);
		$fsi = $this->fsAr[$sd]->add_bitmap_fill($bfObj);

		$this->set_fillFlags($fsi, $fSel);

		return $fsi;
	}

	// add a linestyle, if new linestyle is already stored in lsAr it will
	// refresh currentLSi without adding anything
	function add_lineStyle($lineWidth = 0, $cAr = array()){

		if(is_string($cAr)){

			if(strlen($cAr) > 7){
				$alpha = hexdec(substr($cAr, 7));
				$cAr = parse_color($cAr, $alpha, true);
				$this->update_tagID(SHAPE_ID3);
			}
			else $cAr = get_color_array($cAr);
		}

		if(!count($cAr)){
			$cAr["red"]		= 0;
			$cAr["green"]	= 0;
			$cAr["blue"]	= 0;

			if($this->tagID == SHAPE_ID3) $cAr["alpha"] = 0xff;
		}

		$this->flagNewStyles |= (int) LINE_STYLE;

		$this->currentLSi = $this->lsAr[$this->shapeDepth]->add_lineStyle($lineWidth, $cAr);
		if($this->tagID != SHAPE_ID3 && isset($cAr["alpha"])) $this->update_tagID(SHAPE_ID3);

		return $this->currentLSi;
	}

	// thi is similar to set_currentLSi
	// returns related lineStyle color and width;
	function select_lineStyle_index($lsIndex){

		$this->currentLSi = $lsIndex;
		$lsa = $this->lsAr[$this->shapeDepth]; // lineStyleArray
		$ls  = $lsa->get_lineStyleAt($lsIndex); // lineStyle object
		$lsd = $ls->fetch_assoc();			   // lineStyle data array
		return $lsd;
	}


	// this is a wrapper to stylechange shape record
	// fillStyle1 = right
	// fillStyle0 = left
	function moveTo($mX = 0, $mY = 0, $fill = AUTO_FILL, $line = false, $isd = false){

		$mX = nmb2twip($mX);
		$mY = nmb2twip($mY);

		$fls = $this->flagNewStyles;

		if(!($fls & LINE_STYLE) && $fill == AUTO_FILL)
			if(!($fls & RIGHT_FILL) && !($fls & LEFT_FILL))
					$this->flagNewStyles |= RIGHT_FILL;

		$sd = $this->shapeDepth;
		$this->x = $this->initialX = $mX;
		$this->y = $this->initialY = $mY;
		$this->update_shapeBounds();
		$sri = count($this->srAr);
		$this->srAr[$sri] = new MKStyleChangeRecord("", $this->tagID);
		$this->srAr[$sri]->update_moveTo($mX, $mY, true);

		$fsc = $this->fsAr[$sd]->get_fsCount();
		$lsc = $this->lsAr[$sd]->get_lsCount();
		$fls = $this->flagNewStyles;

		if($fls & (int) LEFT_FILL || $fill & (int) LEFT_FILL){
			$this->srAr[$sri]->update_leftFill($this->currentLFi, $fsc);
		}

		if($fls & (int) RIGHT_FILL || $fill & (int) RIGHT_FILL){
			$this->srAr[$sri]->update_rightFill($this->currentRFi, $fsc);
		}

		if($fls & (int) LINE_STYLE || $line){
			$this->srAr[$sri]->update_lineStyle($this->currentLSi, $lsc);
		}

		// request a stateNewStyles from parameter
		if($isd) $this->increase_shapeDepth();

		// check if stateNewStyles has to be set
		// that is, increase_shapeDepth was invoked
		if($fls & (int) HAS_NEW_STYLE){
			$this->srAr[$sri]->set_stateNewStyles($this->flagNewStyles);
			$this->flagNewStyles ^= (int) HAS_NEW_STYLE;
		}

	}

	// draw a straight line beginning from current coord to relative coords
	// this is a MKstraight_shaperecord wrapper
	function lineTo($lX = null, $lY = null, $absCoord = true){

		$lX = nmb2twip($lX);
		$lY = nmb2twip($lY);

		$sri = count($this->srAr);
		$gc = get_class($this->srAr[$sri - 1]);

		if($gc == "MKStyleChangeRecord"){
			if($this->currentLSi)
				$this->srAr[$sri - 1]->update_lineStyle($this->currentLSi);
		}

		$this->srAr[$sri] = new MKStraightEdgeRecord();

		if(isset($lX)){
			if($absCoord){
				$rx = $this->get_local_coord($lX, "x");
			}
			else {
				$rx = $lX;
				$this->x += $lX;		// update x draw cursor
			}
			$this->srAr[$sri]->update_deltaX($rx, true);
		}

		if(isset($lY)){
			if($absCoord){
				$ry = $this->get_local_coord($lY, "y");
			}
			else {
				$ry = $lY;
				$this->y += $lY;		// update x draw cursor
			}
			$this->srAr[$sri]->update_deltaY($ry, true);
		}

		$this->srAr[$sri]->update_flags(); // sets generic/orto and vert/hor flag
		//$this->update_shapeBounds();
	}

	// draw 2nd order bicubic spline
	// this is a MKcurved_shaperecord wrapper
	function curveTo($cX, $cY, $aX, $aY, $absCoord = true){

		$sri = count($this->srAr);

		$cX = nmb2twip($cX);
		$cY = nmb2twip($cY);
		$aX = nmb2twip($aX);
		$aY = nmb2twip($aY);

		$gc = get_class($this->srAr[$sri - 1]);

		if($gc == "MKStyleChangeRecord"){
			if($this->currentLSi)
				$this->srAr[$sri - 1]->update_lineStyle($this->currentLSi);
		}

		$this->srAr[$sri] = new MKCurvedEdgeRecord("");

		if($absCoord){

			$r1x = $this->get_local_coord($cX, "x");
			$r1y = $this->get_local_coord($cY, "y");
			$r2x = $this->get_local_coord($aX, "x");
			$r2y = $this->get_local_coord($aY, "y");
		}
		else {

			$r1x = $cX;
			$r1y = $cY;
			$r2x = $aX;
			$r2y = $aY;
			$this->x += $aX;
			$this->y += $aY;
		}

		$this->srAr[$sri]->update_data($r1x, $r1y, $r2x, $r2y, true);
	}

	// draw a bezier line from current x, y
	function bezierTo($c1X, $c1Y, $c2X, $c2Y, $a2X, $a2Y, $tolerance = 1, $absCoord = true){
		/*
		$c1X = snap2Twip($c1X);
		$c1Y = snap2Twip($c1Y);
		$c2X = snap2Twip($c2X);
		$c2Y = snap2Twip($c2Y);
		$a2X = snap2Twip($a2X);
		$a2Y = snap2Twip($a2Y);
		*/

		$ctp = array();
		$x = twip2nmb($this->x);
		$y = twip2nmb($this->y);
		$ctp = mc_drawBezier($x, $y, $c1X, $c1Y, $c2X, $c2Y, $a2X, $a2Y, $tolerance);

		$str = "";
		$k = 1;
		foreach($ctp as $m)
			$this->curveTo($m["cx"], $m["cy"], $m["ax"], $m["ay"], $absCoord);
	}

	// draw an arc from current x, y, to ARC ORIGIN x y
	function arcTo($ax, $ay, $radius, $arcSweep, $startAngle, $yRadius, $absCoord = true){

		/*
		$ax = snap2Twip($ax);
		$ay = snap2Twip($ay);
		*/

		$ctp = array();
		$ctp = drawArc ($ax, $ay, $radius, $arcSweep, $startAngle, $yRadius);

		foreach($ctp as $m){
			$this->curveTo($m["cx"], $m["cy"], $m["ax"], $m["ay"], $absCoord);
			//$str .= $k++ .": " .implode($m, ", ") ."<br />";
		}
	}

	//
	function close_path(){
		if($this->initialX != $this->x || $this->initialY != $this->y)
			$this->lineTo($this->initialX, $this->initialY, false);
	}

	// draw a straight line having from and to coords
	function add_line($ax, $ay, $bx, $by, $absCoord = true){

		$this->moveTo($ax,$ay);
		$sri = count($this->srAr) - 1;

		$this->srAr[$sri]->update_lineStyle($this->currentLSi);

		$rx = ($absCoord) ? $bx : $this->get_local_coord($bx, "x");
		$ry = ($absCoord) ? $by : $this->get_local_coord($by, "y");

		$this->lineTo($rx, $ry);
	}

	// draw a bicubic spline
	function add_bicubic_spline($ax, $ay, $bx, $by, $cx, $cy, $absCoord = true){

		$this->moveTo($ax,$ay);
		$sri = count($this->srAr) - 1;

		$this->srAr[$sri]->update_lineStyle($this->currentLSi);

		$r1x = ($absCoord) ? $bx : $this->get_local_coord($bx, "x");
		$r1y = ($absCoord) ? $by : $this->get_local_coord($by, "y");

		$r2x = ($absCoord) ? $cx : $this->get_local_coord($cx, "x");
		$r2y = ($absCoord) ? $cy : $this->get_local_coord($cy, "y");

		$this->curveTo($r1x, $r1y, $r2x, $r2y);
	}


	// draw an arc from x, y
	function add_arc($x, $y, $radius, $arcSweep, $startAngle, $yRadius, $positionX = "center", $positionY = "center", $absCoord = true){

		/* TO DO */
		if($positionX == "left") $x += $radius * 2;
		else if($positionX == "center") $x += $radius;

		if($positionY == "top") $y += $yRadius;
		else if($positionY == "bottom")  $y -= $yRadius;

		$this->moveTo($x, $y);
		$this->arcTo($x, $y, $radius, $arcSweep, $startAngle, $yRadius, $absCoord);
	}

	// draw a 3rd order bezier spline, composed by two 2nd order bicubic spline
	function add_bezier($a1X, $a1Y, $c1X, $c1Y, $c2X, $c2Y, $a2X, $a2Y, $tolerance = 5, $absCoord = true){

		/* implementing */
		$this->moveTo($a1X, $a1Y);

		$ctp = array();
		$ctp = mc_drawBezier($a1X, $a1Y, $c1X, $c1Y, $c2X, $c2Y, $a2X, $a2Y, $tolerance);

		$str = "";
		$k = 1;
		foreach($ctp as $m){
			//$str .= $k++ .": " .implode($m, ".");
			$this->curveTo($m["cx"], $m["cy"], $m["ax"], $m["ay"], $absCoord);
		}
	}

	// draw a rectangle/square
	// isd means: increase shape depth, it is useful to draw correctly even when overlap with other shapes
	function add_rect($x = 0, $y = 0, $width = 100, $height = 100, $fill = AUTO_FILL, $line = false, $isd = false){

		/*
		$this->moveTo($x, $y, $fill, $line, $isd);
		$this->lineTo($x + $width, $y);
		$this->lineTo($x + $width, $y + $height);
		$this->lineTo($x, $y + $height);
		$this->lineTo($x, $y);
		*/

		$this->moveTo($x + $width, $y + $height, $fill, $line, $isd);
		$this->lineTo($x, $y + $height);
		$this->lineTo($x, $y);
		$this->lineTo($x + $width, $y);
		$this->lineTo($x + $width, $y + $height);
	}

	// draw a perfect circle
	function add_circle($radius, $centerX = 0, $centerY = 0, $fill = AUTO_FILL, $line = false, $isd = false){

		// memo: coefficent for bezier approximation
		// k = 0.5522847498;	//$k = 4 * ((sqrt(2) - 1) / 3);
		$r = $radius;

		$q1x = $centerX;
		$q1y = $centerY - $r;
		$this->moveTo($q1x, $q1y, $fill, $line, $isd);

		/*
		rendering of a circle here is done using a look-up table
		faster and non-recursive methods required
		cx			cy			ax			ay
		1.414...	0			1.707...	0.292...
		2 			0.585... 	2 			1
		2 			1.4142... 	1.707... 	1.707...
		1.414... 	2 			1 			2
		0.585... 	2 			0.292... 	1.707...
		0 			1.414... 	0 			1
		0 			0.585... 	0.292... 	0.292...
		0.585... 	0 			1 			0
		*/

		// some precalculated values
		$a1 = $r * 1.4142135623730950488016887242097; // sqrt(2, 2)
		$a2 = $r * 1.7071067811865475244008443621048; // 1 + sin(45)
		$a3 = $r * 0.2928932188134524755991556378953; // .5 / a1
		$a4 = $r * 2;
		$a5 = $r * 0.5857864376269049511983112757905; // 1 / a1

		// coords needed to draw 8 curveto, minimum amount
		// for good rendering, please don't care to the names of vars
		// they are pointless
		$b1 = $q1x  + $a1 - $r;
		$b2 = $q1x  + $a2 - $r;
		$b3 = $q1y  + $a3;
		$b4 = $q1x  + $a4 - $r;
		$b5 = $q1y  + $a5;
		$b6 = $q1y  + $r;
		$b7 = $q1y  + $a1;
		$b8 = $q1y  + $a2;
		$b9 = $q1y  + $a4;
		$b10 = $q1x + $a5 - $r;
		$b11 = $q1x + $a3 - $r;
		$b12 = $q1x - $r;

		$this->curveTo($b1,  $q1y, $b2,  $b3);
		$this->curveTo($b4,  $b5,  $b4,  $b6);
		$this->curveTo($b4,  $b7,  $b2,  $b8);
		$this->curveTo($b1,  $b9,  $q1x, $b9);
		$this->curveTo($b10, $b9,  $b11, $b8);
		$this->curveTo($b12, $b7,  $b12, $b6);
		$this->curveTo($b12, $b5,  $b11, $b3);
		$this->curveTo($b10, $q1y, $q1x, $q1y);
	}

	// draw an oval, if width == height draw a perfect circle
	// this method is smarter than add_circle, because positionX and positionY
	// but it needs some additional computational work
	function add_ellipse($x, $y, $width, $height, $positionX = "center", $positionY = "center", $fill = AUTO_FILL, $line = false, $isd = false){

		if($positionX == "left") $x += $width * 2;
		else if($positionX == "center") $x += $width / 2;

		if($positionY == "top") $y += $height;
		else if($positionY == "bottom")  $y -= $height;

		$this->moveTo($x, $y, $fill, $line, $isd);
		$this->arcTo($x, $y, $width / 2, -360, 0, $height / 2);
	}

	// draw a polygon from given points stored in arrays cx cy
	// and its coords, the first item in array must be the first drawing point
	// array items are associative array with: x, y field
	// this method provides to close path if last coord is different from first
	function add_straight_poly($pAr, $fill = AUTO_FILL, $line = false, $isd = false){

		$p0 = $pAr[0];
		$this->moveTo($p0["x"], $p0["y"], $fill, $line, $isd);

		$pLength = count($pAr);

		for($i = 1; $i < $pLength; $i++){
			$p = $pAr[$i];
			$tx = $p["x"]; $ty = $p["y"];
			$this->lineTo($tx, $ty);
		}

		if($tx != $p0["x"] && $ty != $p0["y"])
			$this->lineTo($p0["x"], $p0["y"]); // close the path
	}


	// draw a polygon from a given points stored in array pAr
	// each item can contain variable number of fields:
	// line and first record: x, y
	// curve: cx, cy, ax, ay
	// bezier: c1x, c1y, c2x, c2y, ax, ay
	// this method provides to close path if last coord is different from first
	function add_mixed_poly($pAr, $lFill = false, $fill = AUTO_FILL, $line = false, $isd = false){

		$this->moveTo($pAr[0]["x"], $pAr[0]["y"], $fill, $line, $isd);
		$this->initialX = $this->x;
		$this->initialY = $this->y;

		$pCount = count($pAr);

		for($i = 1; $i < $pCount; $i++){

			$p = $pAr[$i];
			$cCount = count($p);

			$p = $pAr[$i];
			$cLength = count($p);

			if(array_key_exists("rx", $p) && count($p) == 4){ // arc XPERIMENTAL MUST BE TESTED

				$tx = $p["x"]; $ty = $p["y"];
				$this->arcTo($p["x"], $p["y"], $p["rx"], 45, 0, $p["ry"]);

				continue;
			}
			if(array_key_exists("c1x", $p) && count($p) == 6){ // bezier

				$tx = $p["ax"]; $ty = $p["ay"];
				$this->bezierTo($p["c1x"], $p["c1y"], $p["c2x"], $p["c2y"], $p["ax"], $p["ay"]);

				continue;
			}

			if(array_key_exists("cx", $p) && count($p) == 4){ // curve
				$tx = $p["ax"]; $ty = $p["ay"];
				$this->curveTo($p["cx"], $p["cy"], $p["ax"], $p["ay"]);

				continue;
			}

			if(array_key_exists("x", $p) && count($p) == 2){ // line
				$tx = $p["x"]; $ty = $p["y"];
				$this->lineTo($p["x"], $p["y"]);
			}
		}

		if($tx != $pAr[0]["x"] && $ty != $pAr[0]["y"]) {
			$this->lineTo($pAr[0]["x"], $pAr[0]["y"]); // close the path
		}
		$this->close_path();
	}

	// draw polygon
	function add_polygon($centerX, $centerY, $radius = 100, $sides = 3, $rotation = 0){
		$pc = get_coords_polygon($centerX, $centerY, $radius, $sides, $rotation);
		$this->add_straight_poly($pc);
	}

	//
	function add_star($centerX, $centerY, $radiusExt = 0, $radiusInt = 0, $spikes = 5, $rotation = 0){
		$sc = get_coords_star($centerX, $centerY, $radiusExt, $radiusInt, $spikes, $rotation);
		$this->add_straight_poly($sc);
	}

	// very experimental, draw non closed path
	function add_sketch($pAr){

		$lFill = false;
		$fill = LINE_STYLE;
		$line = true;
		$isd = false;

		if(!count($pAr)) return false;

		$this->moveTo($pAr[0]["x"], $pAr[0]["y"], $fill, $line, $isd);
		$this->initialX = $this->x;
		$this->initialY = $this->y;

		$pLength = count($pAr);

		for($i = 1; $i < $pLength; $i++){

			$p = $pAr[$i];
			$cLength = count($p);

			if(array_key_exists("rx", $p) && count($p) == 4){ // arc XPERIMENTAL MUST BE TESTED

				$tx = $p["x"]; $ty = $p["y"];
				$this->arcTo($p["x"], $p["y"], $p["rx"], 45, 0, $p["ry"]);

				continue;
			}
			if(array_key_exists("c1x", $p) && count($p) == 6){ // bezier

				$tx = $p["ax"]; $ty = $p["ay"];
				$this->bezierTo($p["c1x"], $p["c1y"], $p["c2x"], $p["c2y"], $p["ax"], $p["ay"]);

				continue;
			}

			if(array_key_exists("cx", $p) && count($p) == 4){ // curve
				$tx = $p["ax"]; $ty = $p["ay"];
				$this->curveTo($p["cx"], $p["cy"], $p["ax"], $p["ay"]);

				continue;
			}

			if(array_key_exists("x", $p) && count($p) == 2){ // line
				$tx = $p["x"]; $ty = $p["y"];
				$this->lineTo($p["x"], $p["y"]);
			}
		}
	}

	// set translation data for placeObject2
	function move($x, $y, $depthFlag = false, $isChange = false){
		return $this->po->set_position($x, $y, $depthFlag, $isChange);
	}

	// set scale data for placeObject2
	function scale($scaleX, $scaleY, $depthFlag = false, $isChange = false){
		return $this->po->set_scale($scaleX, $scaleY, $depthFlag, $isChange);
	}

	// set rotation data for placeObject2
	function rotate($rotation, $depthFlag = false, $isChange = false){
		return $this->po->set_rotation($rotation, $depthFlag, $isChange);
	}

	// mask other graphic objects
	function set_mask($maskDepth = 1){ $this->po->set_clipDepth($maskDepth); }

	//
	function add_matrix($mad = array()){ $this->po->add_matrix($mad); }

	// return effective byte length of shape container
	// (DefineShape[2|3])
	function get_length(){
		$this->update_tagData();
		return strlen($this->tagData);
	}

	//
	function fetch_xml($includeHeader = true, $tablevel = 0, $includeHexData = false, $nodeName = "shape"){

		/* TODO: implement fetch_xml */
		$this->nodeName = $nodeName;
		$xmlStr = "";
		return $xmlStr;
	}
}



/**
 * FILLSTYLEARRAY
 */
class MKFillStyleArray {

	var $buf;
	var $tagID;
	var $fsCount; // fillStyle Count
	var $fStyles = array(); // fill styles

	// the constructor
	function MKFillStyleArray($buf, $tagID = SHAPE_ID){

		$this->tagID = $tagID;

		if(strlen($buf)){
			$this->buf = $buf;
			$this->get_fillStyle_array_data();
		}
	}

	//
	function set_buf($buf){ $this->buf = $buf; }
	function get_buf() { return $this->buf; }

	function set_tagID($tagID){ $this->tagID = $tagID; }
	function get_tagID() { return $this->tagID; }

	function set_fsCount($fsCount) { $this->fsCount = $fsCount; }
	function get_fsCount() {
		$this->fsCount = count($this->fStyles);
		return $this->fsCount;
	}

	function set_fStyles($fStyles) { $this->fStyles = $fStyles; }
	function get_fStyles() { return $this->fStyles; }

	// return fillStyle object at given index
	// note that fsIndex must be referenced from 1
	// as intended by swf specification
	function get_fillStyleAt($fsIndex){ return $this->fStyles[$fsIndex]; }

	function set_fillStyle_array_data($fsad = array()){

		if(count($fsad)){
			$this->get_fillStyle_array_data($fsad);
		}
		else{
			$fsad = array();
			$fsad = $this->fetch_assoc();
		}

		$this->buf = write_fillStyleArray_data($fsad);

		return $this->buf;
	}

	// fsad is an optional associative array with all field
	function get_fillStyle_array_data($fsad = array()){

		if(!count($fsad)){
			$fsad = array();
			$fsad = read_fillStyleArray_data($this->buf, $this->tagID);
		}

		$this->fsCount = $fsad["fillStyleCount"];

		for($i = 0; $i < $this->fsCount; $i++){

			$fsd = $fsad["fillStyles"][$i];

			$this->fStyles[$i] = new MKFillStyle("", $this->tagID);
			$this->fStyles[$i]->set_fillStyleType($fsd["fillStyleType"]);

			if(isset($fsd["fillColor"])) $this->fStyles[$i]->set_color($fsd["fillColor"]);

			if(isset($fsd["gradient"])){
				$this->fStyles[$i]->set_gradientMatrix($fsd["gradientMatrix"]);
				$this->fStyles[$i]->set_gradient($fsd["gradient"]);
			}

			if(isset($fsd["bitmapID"])){
				$this->fStyles[$i]->set_bitmapID($fsd["bitmapID"]);
				$this->fStyles[$i]->set_bitmapMatrix($fsd["bitmapMatrix"]);
			}
		}

		//if(strlen($this->buf)) $this->buf = substr($this->buf, 0, $fsad["fillStyleArrayLength"]); // strip exceeding bytes
	}


	//
	function update_tagID($tagID){

		if($this->tagID == $tagID) return;

		$this->tagID = $tagID;

		if(count($this->fStyles)){
			foreach($this->fStyles as $fStyle) $fStyle->update_tagID($tagID);
		}
	}

	//
	function add_fillStyle_solid($cAr = array()){

		$fsi = count($this->fStyles);

		if(isset($cAr["alpha"])){
			if($this->tagID != SHAPE_ID3)
				$this->update_tagID(SHAPE_ID3);
		}

		$fsDup = $this->find_fillStyle_solid_duplicate($cAr);

		if($fsDup) return $fsDup; // avoid to add a lineStyle duplicate

		$this->fStyles[$fsi] = new MKFillStyle("", $this->tagID);
		$this->fStyles[$fsi]->set_fillStyleType(SOLID_FILL);
		$this->fStyles[$fsi]->set_color($cAr);

		$fsi++;

		$this->fsCount = $fsi;

		return $fsi; // fillStyle added, return its index starting from 1
	}

	//
	function find_fillStyle_solid_duplicate($cAr){

		$fsDup = 0;
		$cStr = implode($cAr, ".");

		for($i = 0; $i < $this->fsCount; $i++){

			$fStyle = $this->fStyles[$i];
			if($fStyle->get_fillStyleType() != SOLID_FILL) continue;

			$fStr = implode($fStyle->color->fetch_assoc(), ".");

			if($fStr == $cStr){
				$fsDup = $i + 1;
				break;
			}
		}

		return $fsDup;
	}

	//
	function add_gradient_fill($gfObj){

		$fsi = count($this->fStyles);
		$this->fStyles[$fsi] = $gfObj;
		$this->fsCount = count($this->fStyles);

		return $this->fsCount;
	}

	//
	function add_bitmap_fill($bfObj){

		$fsi = count($this->fStyles);
		$this->fStyles[$fsi] = $bfObj;
		$this->fsCount = count($this->fStyles);

		return $this->fsCount;
	}

	// returns an associative array with keys from class members
	function fetch_assoc(){

		$fsad = array();

		$fsad["fillStyleCount"] = $this->get_fsCount();
		$fsad["fillStyles"] = array();

		foreach($this->fStyles as $fStyle){

			$fli = count($fsad["fillStyles"]);
			//$fsType = $fStyle->get_fillStyleType();

			$fsad["fillStyles"][$fli] = array();
			$fsad["fillStyles"][$fli] = $fStyle->fetch_assoc();
		}

		return $fsad;
	}

	// return buf, refresh from its members
	function update_buf(){ return $this->set_fillStyle_array_data(); }

	// returns FILSTYLEARRAY byte length
	function get_length(){

		$length = 1;
		if(!$this->fsCount) return $length;
		if($this->fsCount >= 0xFF) $length += 2;

		foreach($this->fStyles as $fStyle) $length += $fStyle->get_length();

		return $length;
	}
}

/**
 * FILLSTYLE
 */
class MKFillStyle {

	var $buf;
	var $tagID;
	var $fillStyleType = SOLID_FILL;
	var $color;
	var $gradientMatrix;
	var $gradient;
	var $bitmapID;
	var $bitmapMatrix;

	// the constructor
	function fillStyle($buf, $tagID = SHAPE_ID){

		$this->tagID = $tagID;

		if(strlen($buf)){
			$this->buf = $buf;
			$this->get_fillStyle_data();
		}
	}

	//
	function set_buf($buf){	$this->buf = $buf; }
	function get_buf(){ return $this->buf; }

	//
	function set_fillStyleType($fillStyleType) { $this->fillStyleType = $fillStyleType; }
	function get_fillStyleType() { return $this->fillStyleType; }

	function set_color($cAr) {

		if(isset($cAr["alpha"])) $this->tagID = SHAPE_ID3;

		$this->color = new MKColor("", $this->tagID);
		$this->color->set_color($cAr);
	}
	function get_color() { return $this->color; }

	// drawFromCenter flag set to move default gradient
	// using center origin instead left,top coord
	function set_gradientMatrix($gm, $drawFromCenter = false) {

		if(is_array($gm)){

			$mTwips = 32768;
			$this->gradientMatrix = new MKMatrix();

			$px = $gm["x"];
			$py = $gm["y"];

			if($this->fillStyleType == LINEAR_GRADIENT_FILL){

				$px = $gm["x"];
				$py = $gm["y"];
				//$mTwips /= 2;

				if(!$drawFromCenter) {
					$px += $gm["width"] / 2;
					$py += $gm["height"] / 2;
					//$mTwips *= 2;
				}
			}

			$this->gradientMatrix->set_position($px, $py);
			$twipWidth = nmb2twip($gm["width"]);
			$twipHeight = nmb2twip($gm["height"]);

			$scalex = 1;
			$scaley = 1;
			if($twipWidth)  $scalex = 100 / round($mTwips / $twipWidth);
			if($twipHeight) $scaley = 100 / round($mTwips / $twipHeight);

			$this->gradientMatrix->set_scale($scalex, $scaley);

			$rotation = isset($gm["rotation"]) ? $gm["rotation"] : 0;

			if($rotation) $this->gradientMatrix->set_rotation($gm["rotation"]);
		}
		else $this->gradientMatrix = $gm;
	}
	function get_gradientMatrix() { return $this->gradientMatrix; }

	//
	function set_gradient($gad) {

		if(is_array($gad)){
			$this->gradient = new MKGradient();
			$this->gradient->set_colors($gad["colors"]);
			$this->gradient->set_ratios($gad["ratios"]);
		}
		else $this->gradient = $gad;
	}
	function get_gradient() { return $this->gradient; }

	//
	function set_bitmapID($bitmapID) { $this->bitmapID = $bitmapID; }
	function get_bitmapID() { return $this->bitmapID; }

	//
	function set_bitmapMatrix($bitmapMatrix = array(), $bitmapID = 0) {

		if($bitmapID) $this->bitmapID = $bitmapID;

		if(is_array($bitmapMatrix)) {

			$this->bitmapMatrix = new MKMatrix("", true);

			// x and y field must be set
			$x = isset($bitmapMatrix["x"]) ? $bitmapMatrix["x"] : 0;
			$y = isset($bitmapMatrix["y"]) ? $bitmapMatrix["y"] : 0;

			if($x || $y ) {
				$this->bitmapMatrix->set_position($bitmapMatrix["x"], $bitmapMatrix["y"]);
			}

			$sx = 1; $sy = 1;

			$sx = isset($bitmapMatrix["scaleX"]) ? $bitmapMatrix["scaleX"] : 1;
			$sy = isset($bitmapMatrix["scaleY"]) ? $bitmapMatrix["scaleY"] : 1;

			$this->bitmapMatrix->set_scale($sx * 2000, $sy * 2000, true);

			$rotation = isset($bitmapMatrix["rotation"]) ? $bitmapMatrix["rotation"] : 0;
			if($rotation) $this->bitmapMatrix->set_rotation($rotation);
		}
		else $this->bitmapMatrix = $bitmapMatrix;

	}
	function get_bitmapMatrix() { return $this->bitmapMatrix; }

	//
	function set_fillStyle_data($fsd = array()){

		if(count($fsd)){
			$this->get_fillStyle_data($fsd);
		}
		else{
			$fsd = array();
			$fsd = $this->fetch_assoc();
		}

		$this->buf = write_fillStyle_data($fsd, $this->tagID);
		return $this->buf;
	}

	//
	function get_fillStyle_data($fsd = array()){

		if(!count($fsd)){
			$fsd = array();
			$fsd = read_fillStyle_data($this->buf);
		}

		$this->fillStyleType = $fsd["fillStyleType"];

		if($fsd["fillStyleType"] == SOLID_FILL){
			$this->color = new MKColor();
			$this->color->set_color($fsd["fillColor"]);

		}
		else if($fsd["fillStyleType"] == LINEAR_GRADIENT_FILL ||
				$fsd["fillStyleType"] == RADIAL_GRADIENT_FILL){

			$this->gradientMatrix = new MKMatrix();
			$this->gradientMatrix->set_matrix_data($fsd["gradientMatrix"]);

			$this->gradient = new MKGradient();
			$this->gradient->set_gradient_data($fsd["gradient"]);
		}
		else if($fsd["fillStyleType"] == REPEATING_BITMAP_FILL ||
				$fsd["fillStyleType"] == CLIPPED_BITMAP_FILL ||
				$fsd["fillStyleType"] == NS_BITMAP_FILL ||
				$fsd["fillStyleType"] == NS_CLIPPED_BITMAP_FILL){

			$this->bitmapMatrix = new MKMatrix();
			$this->bitmapMatrix->set_matrix_data($fsd["bitmapMatrix"]);
			$this->bitmapID = $fsd["bitmapID"];
		}

		if(strlen($this->buf)) $this->buf = substr($this->buf, 0, $fsd["fillStyleLength"]); // strip unused bytes
	}

	//
	function update_tagID($tagID){

		if($this->tagID == $tagID) return;

		$this->tagID = $tagID;

		if(isset($this->color)) $this->color->update_tagID($tagID);
		//if(isset($this->gradient)) $this->gradient->update_tagID($tagID);
	}

	// returns an associative array with class members
	// as keys
	function fetch_assoc(){

		$fsd = array();

		$fsd["fillStyleType"] = $this->fillStyleType;

		if($fsd["fillStyleType"] == SOLID_FILL){
			$fsd["fillColor"] = $this->color->get_color();
		}
		else if($fsd["fillStyleType"] == LINEAR_GRADIENT_FILL ||
				$fsd["fillStyleType"] == RADIAL_GRADIENT_FILL){

			$fsd["gradientMatrix"] = $this->gradientMatrix->fetch_assoc(true);
			$fsd["gradient"] = $this->gradient->fetch_assoc();
		}
		else if($fsd["fillStyleType"] == REPEATING_BITMAP_FILL ||
				$fsd["fillStyleType"] == CLIPPED_BITMAP_FILL ||
				$fsd["fillStyleType"] == NS_BITMAP_FILL ||
				$fsd["fillStyleType"] == NS_CLIPPED_BITMAP_FILL){

			$fsd["bitmapMatrix"] = $this->bitmapMatrix->fetch_assoc(true);
			$fsd["bitmapID"] = $this->bitmapID;
		}

		return $fsd;
	}

	// return buf, refresh from its members
	// alias of set_fillStyle_data
	function update_buf(){ return $this->set_fillStyle_data(); }

	// returns FILLSTYLE byte length
	function get_length(){

		$length = 1;

		if($this->fillStyleType == SOLID_FILL) {
			$length += 3;
			if($this->tagID == SHAPE_ID3) $length++;
		}
		else if($this->fillStyleType == LINEAR_GRADIENT_FILL ||
				$this->fillStyleType == RADIAL_GRADIENT_FILL) {

			$length += $this->gradientMatrix->get_length();
			$length += $this->gradient->get_length();

		}
		else if($this->fillStyleType == REPEATING_BITMAP_FILL ||
				$this->fillStyleType == CLIPPED_BITMAP_FILL ||
				$this->fillStyleType == NS_BITMAP_FILL ||
				$this->fillStyleType == NS_CLIPPED_BITMAP_FILL) {

			$length += 2;
			$length += $this->bitmapMatrix->get_length();
		}

		return $length;
	}
}

/**
 * LINESTYLEARRAY
 */
class MKLineStyleArray {

	var $buf;
	var $tagID;
	var $lsCount; // lineStyle Count
	var $lStyles = array(); // line styles

	// the constructor
	function MKLineStyleArray($buf, $tagID = SHAPE_ID){

		$this->tagID = $tagID;

		if(strlen($buf)){
			$this->buf = $buf;
			$this->get_lineStyle_array_data();
		}
	}

	//
	function set_buf($buf){ $this->buf = $buf; }
	function get_buf() { return $this->buf; }

	function set_tagID($tagID){ $this->tagID = $tagID; }
	function get_tagID() { return $this->tagID; }

	function set_lsCount($lsCount) { $this->lsCount = $lsCount; }
	function get_lsCount() {
		$this->lsCount = count($this->lStyles);
		return $this->lsCount;
	}

	function set_lStyles($lStyles) { $this->lStyles = $lStyles; }
	function get_lStyles() {
		$this->lsCount = count($this->lStyles);
		return $this->lStyles;
	}

	// return lineStyle object at given index
	// note that lsIndex must be referenced from 1
	// as intended by swf specification
	function get_lineStyleAt($lsIndex){ return $this->lStyles[$lsIndex]; }

	function set_lineStyle_array_data($lsad = array()){

		if(count($lsad)){
			$this->get_lineStyle_array_data($lsad);
		}
		else{
			$lsad = array();
			$lsad = $this->fetch_assoc();
		}

		$this->buf = write_lineStyleArray_data($lsad, $this->tagID);

		return $this->buf;
	}

	//
	function get_lineStyle_array_data($lsad = array()){

		if(!count($lsad)){
			$lsad = array();
			$lsad = read_lineStyleArray_data($this->buf, $this->tagID);
		}

		$this->lsCount = $lsad["lineStyleCount"];

		for($i = 0; $i < $this->lsCount; $i++) {

			$lsd = $lsad["lineStyles"][$i];
			$this->lStyles[$i] = new MKLineStyle("", $this->tagID);
			$this->lStyles[$i]->set_color($lsd["lineColor"]);
			$this->lStyles[$i]->set_lineWidth($lsd["lineWidth"]);
		}

		if(strlen($this->buf)) $this->buf = substr($this->buf, 0, $lsad["lineStyleArrayLength"]); // strip exceeding bytes
	}

	//
	function update_tagID($tagID){

		if($this->tagID == $tagID) return;

		$this->tagID = $tagID;

		if(count($this->lStyles)){
			foreach($this->lStyles as $lStyle)
				$lStyle->update_tagID($tagID);
		}
	}

	//
	function add_lineStyle($lineWidth = 0, $cAr = array()){

		if(!count($cAr)){
			$cAr["red"] = 0;
			$cAr["blue"] = 0;
			$cAr["green"] = 0;

			if($this->tagID == SHAPE_ID3)
				$cAr["alpha"] = 0xff;
		}

		$lsi = count($this->lStyles);

		if(isset($cAr["alpha"])) $this->tagID = SHAPE_ID3;

		$lw = nmb2twip($lineWidth);
		$lsDup = $this->find_lineStyle_duplicate($lw, $cAr);

		if($lsDup) return $lsDup; // avoid to add a lineStyle duplicate

		$this->lStyles[$lsi] = new MKLineStyle("", $this->tagID);
		$this->lStyles[$lsi]->set_color($cAr);
		$this->lStyles[$lsi]->set_lineWidth($lw);
		$lsi++;

		$this->lsCount = $lsi;

		return $lsi; // lineStyle added, return its index starting from 1
	}

	//
	function find_lineStyle_duplicate($lineWidth, $cAr){

		$lsDup = 0;
		$cStr = implode($cAr, ".") ."." .$lineWidth;

		$this->lsCount = count($this->lStyles);

		for($i = 0; $i < $this->lsCount; $i++){

			$lStyle = $this->lStyles[$i];
			$lStr = implode($lStyle->color->fetch_assoc(), ".") ."." .$lStyle->get_lineWidth();

			if($lStr == $cStr){
				$lsDup = $i + 1;
				break;
			}
		}

		return $lsDup;
	}

	// returns an associative array width class members
	// as keys
	function fetch_assoc(){

		$this->lsCount = count($this->lStyles);
		$lsad = array();
		$lsad["lineStyleCount"] = $this->lsCount;
		$lsad["lineStyles"] = array();

		foreach($this->lStyles as $lStyle){

			$lsi = count($lsad["lineStyles"]);
			$lsad["lineStyles"][$lsi] = array();
			$lsad["lineStyles"][$lsi] = $lStyle->fetch_assoc();
		}

		return $lsad;
	}

	// return buf, refresh from its members
	// this is an alias of set_fillStyle_array_data
	function update_buf(){ return $this->set_lineStyle_array_data(); }


	// returns LINESTYLEARRAY bytes length
	function get_length(){

		$length = 1;
		if(!$this->lsCount) return $length;
		if($this->lsCount >= 0xFF) $length += 2;

		$length += $this->lsCount * $this->lStyles[0]->get_length();

		return $length;
	}
}


/**
 * LINESTYLE
 */
class MKLineStyle {

	var $buf;
	var $tagID;
	var $lineWidth;
	var $color;

	// the constructor
	function MKLineStyle($buf, $tagID = SHAPE_ID){

		$this->tagID = $tagID;

		if(strlen($buf)){
			$this->buf = $buf;
			$this->get_lineStyle_data();
		}
	}

	//
	function set_buf($buf){
		$this->buf = $buf;
		$this->get_lineStyle_data();
	}
	function get_buf(){ return $this->buf; }

	//
	function set_lineWidth($lineWidth) { $this->lineWidth = $lineWidth; }
	function get_lineWidth() { return $this->lineWidth; }

	function set_color($cAr) {

		if(isset($cAr["alpha"])) $this->tagID = SHAPE_ID3;
		$this->color = new MKColor("", $this->tagID);
		$this->color->set_color($cAr);
	}
	function get_color() { return $this->color; }

	//
	function set_lineStyle_data($lsd = array()){

		if(count($lsd)){
			$this->get_lineStyle_data($lsd);
		}
		else {

			$lsd = array();
			$lsd = $this->fetch_assoc();
		}

		$this->buf = write_lineStyle_data($lsd);

		return $this->buf;
	}

	//
	function get_lineStyle_data($lsd = array()){

		if(!count($lsd)){
			$lsd = array();
			$lsd = read_lineStyle_data($this->buf, $this->tagID);
		}

		$this->lineWidth = $lsd["lineWidth"];
		$this->color = new MKColor("", $this->tagID);
		$this->color->set_color($lsd["lineColor"]);

		if(strlen($this->buf)) $this->buf = substr($this->buf, 0, $lsd["lineStyleLength"]);
	}

	//
	function update_tagID($tagID){

		if($this->tagID == $tagID) return;

		$this->tagID = $tagID;
		if(isset($this->color)) $this->color->update_tagID($tagID);
	}

	// returns an associative array width class members
	// as keys
	function fetch_assoc(){

		$lsd = array();

		$lsd["lineWidth"] = $this->lineWidth;
		$lsd["lineColor"] = array();
		$lsd["lineColor"] = $this->color->fetch_assoc();

		return $lsd;
	}

	// return buf, refresh from its members
	// this is an alias of set_lineStyle_data
	function update_buf(){ return $this->set_lineStyle_data(); }


	// returns LINESTYLE bytes length
	function get_length(){ return ($this->tagID == SHAPE_ID3) ? 6 : 5; }
}


/**
 * LINESTYLE2
 * swf8
 */
class MKLineStyle2 {

	var $buf;
	var $tagID = SHAPE_ID4;

	var $startCapStyle;			// UB[2] Start cap style:
								// 0 = Round cap
								// 1 = No cap
								// 2 = Square cap

	var $joinStyle;				// UB[2] Join style:
								// 0 = Round join
								// 1 = Bevel join
								// 2 = Miter join
	var $hasFillFlag;			// UB[1] If 1, fill is defined in FillType.
								// If 0, uses Color field.
	var $hoHScaleFlag;			// UB[1] If 1, stroke thickness will not
								// scale if the object is scaled horizontally.
	var $noVScaleFlag;			// UB[1] If 1, stroke thickness will not
								// scale if the object is scaled vertically.
	var $pixelHintingFlag;		// UB[1] If 1, all anchors will be aligned
								// to full pixels.
	var $reserved = 0;			// UB[5] Must be 0.
	var $noClose;				// UB[1] If 1, stroke will not be closed if
								// the stroke�s last point
								//matches its first point. Flash
								// Player will apply caps instead of a join.
	var $endCapStyle;			// UB[2] End cap style:
								// 0 = Round cap
								// 1 = No cap
								// 2 = Square cap
	var $miterLimitFactor;		// If JoinStyle = 2, UI16 Miter limit factor is an 8.8
								// fixed-point value.
	var $color;					// If HasFillFlag = 0,
								// RGBA Color value including alpha channel.
	var $fillType;				// If HasFillFlag = 1,
								// FILLSTYLE Fill style for this stroke.

	// the constructor
	function MKLineStyle2($buf){

		if(strlen($buf)){
			$this->buf = $buf;
			$this->get_lineStyle2_data();
		}
	}

	//
	function set_buf($buf){
		$this->buf = $buf;
		$this->get_lineStyle2_data();
	}
	function get_buf(){ return $this->buf; }

	function set_color($cAr) {

		if(isset($cAr["alpha"])) $this->tagID = SHAPE_ID3;
		$this->color = new MKColor("", $this->tagID);
		$this->color->set_color($cAr);
	}
	function get_color() { return $this->color; }

	//
	function set_lineStyle2_data($lsd = array()){

		if(count($lsd)){
			$this->get_lineStyle2_data($lsd);
		}
		else {
			$lsd = array();
			$lsd = $this->fetch_assoc();
		}

		$this->buf = write_lineStyle2_data($lsd);

		return $this->buf;
	}

	//
	function get_lineStyle2_data($lsd = array()){

		if(!count($lsd)){
			$lsd = array();
			$lsd = read_lineStyle2_data($this->buf, $this->tagID);
		}

		$this->lineWidth = $lsd["lineWidth"];
		$this->color = new MKColor("", $this->tagID);
		$this->color->set_color($lsd["lineColor"]);

		if(strlen($this->buf)) $this->buf = substr($this->buf, 0, $lsd["lineStyleLength"]);
	}

	// returns an associative array width class members
	// as keys
	function fetch_assoc(){

		$lsd = array();

		$lsd["lineWidth"] = $this->lineWidth;
		$lsd["lineColor"] = array();
		$lsd["lineColor"] = $this->color->fetch_assoc();

		return $lsd;
	}

	// return buf, refresh from its members
	// this is an alias of set_lineStyle_data
	function update_buf(){ return $this->set_lineStyle_data(); }


	// returns LINESTYLE bytes length
	function get_length(){ return ($this->tagID == SHAPE_ID3) ? 6 : 5; }
}



///////////////////////////////////////////////////////////////////////////////
// SHAPE RECORDS
// note that to read shape records they need a bit_offset
// since they are ALL BITFIELD disposed
///////////////////////////////////////////////////////////////////////////////

/**
 * STYLECHANGERECORD
 * this is a very special shape record
 * contains some particular methods
 * since it may sets an huge amount of options
 */
class MKStyleChangeRecord {

	var $buf;
	var $tagID;
	var $binStr;
	var $typeFlag = 0;
	var $stateNewStyles = 0;
	var $stateLineStyle = 0;
	var $stateFillStyle1 = 0;
	var $stateFillStyle0 = 0;
	var $stateMoveTo = 0;
	var $nBitsMoveTo = 0;
	var $moveX;
	var $moveY;
	var $fillStyle0;
	var $fillStyle1;
	var $lineStyle;

	var $fsBits = 0; // fill Index bits
	var $lsBits = 0; // line Index bits

	// this is in case of new fill/line style array
	var $fsaData = array();
	var $lsaData = array();
	var $nFillBits = 0;
	var $nLineBits = 0;

	// the constructor
	function MKStyleChangeRecord($buf, $fsBits = 0, $lsBits = 0, $offset = 0, $tagID = SHAPE_ID){

		$this->tagID = $tagID;
		$this->buf = $buf;
		$this->fsBits = $fsBits;
		$this->lsBits = $lsBits;

		$this->get_stylechange_record_data($offset);
	}

	//
	function set_buf($buf) { $this->buf = $buf; }
	function get_buf() { return $this->buf; }

	function set_tagID($tagID) { $this->tagID = $tagID; }
	function get_() { return $this->tagID; }

	function set_binStr($binStr) {$this->binStr = $binStr; }
	function get_binStr() { return $this->binStr; }

	function set_stateNewStyles($flagNewStyles) {

		if($flagNewStyles == NO_STYLE){
			$this->stateNewStyles = 0;
			$this->stateLineStyle = 0;
			$this->stateFillStyle1 = 0;
			$this->stateFillStyle1 = 0;
			return;
		}

		if($flagNewStyles & HAS_NEW_STYLE)$this->stateNewStyles = 1;
		if($flagNewStyles & RIGHT_FILL) $this->stateFillStyle1 = 1;
		if($flagNewStyles & LEFT_FILL) $this->stateFillStyle0 = 1;
		if($flagNewStyles & LINE_STYLE) $this->stateLineStyle = 1;
	}
	function get_stateNewStyles() { return $this->stateNewStyles; }

	function set_stateLineStyle($stateLineStyle){ $this->stateLineStyle = $stateLineStyle; }
	function get_stateLineStyle() { return $this->stateLineStyle; }

	function set_stateFillStyle1($stateFillStyle1) { $this->stateFillStyle1 = $stateFillStyle1; }
	function get_stateFillStyle1() { return $this->stateFillStyle1; }

	function set_stateFillStyle0($stateFillStyle0) { $this->stateFillStyle1 = $stateFillStyle0; }
	function get_stateFillStyle0() { return $this->stateFillStyle0; }

	function set_stateMoveTo($stateMoveTo) { $this->stateMoveTo = $stateMoveTo; }
	function get_stateMoveTo() { return $this->stateMoveTo; }

	function set_nBitsMoveTo($nBitsMoveTo) { $this->nBitsMoveTo = $nBitsMoveTo; }
	function get_nBitsMoveTo() { return $this->nBitsMoveTo; }

	function set_moveX($moveX) { $this->moveX = $moveX; }
	function get_moveX() { return $this->moveX; }

	function set_moveY($moveY) { $this->moveY = $moveY; }
	function get_moveY() { return $this->moveY; }

	function set_fillStyle1($fillStyle1) { $this->fillStyle1 = $fillStyle1; }
	function get_fillStyle1() { return $this->fillStyle1; }

	function set_fillStyle0($fillStyle0) { $this->fillStyle0 = $fillStyle0; }
	function get_fillStyle0() { return $this->fillStyle0; }

	function set_lineStyle($lineStyle) { $this->lineStyle = $lineStyle; }
	function get_lineStyle() { return $this->lineStyle; }

	function set_fsBits($fsBits) { $this->fsBits = $fsBits; } // fill Index bits
	function get_fsBits() { return $this->fsBits; } // fill Index bits

	function set_lsBits($lsBits) { $this->lsBits = $lsBits; } // line Index bits
	function get_lsBits() { return $this->lsBits; } // line Index bits

	function set_fsaData($fsaData) {$this->fsaData = $fsaData; }
	function get_fsaData() { return $this->fsaData; }

	function set_lsaData($lsaData) { $this->lsaData = $lsaData; }
	function get_lsaData() { return $this->lsaData; }

	function set_nFillBits($nFillBits) { $this->nFillBits = $nFillBits; }
	function get_nFillBits() { return $this->nFillBits; }

	function set_nLineBits($nLineBits) { $this->nLineBits = $nLineBits; }
	function get_nLineBits() { return $this->nLineBits; }

	function set_stylechange_record_data($scr = array(), $offset = 0){

		if(count($scr)){
			$this->get_stylechange_record_dat($scr);
		}
		else{
			$scr = array();
			$scr = $this->fetch_assoc();
		}

		// NOTICE
		// binStr is valid only when an appropriate bit offset
		// is given, that is, may easily change when computing
		// in a shape records context
		$this->binStr = write_stylechange_record_data($scr, true, $offset);

		return $this->binStr;
	}

	function get_stylechange_record_data($offset = 0, $scr = array()){

		if(!count($scr)){
			$scr = array();
			$scr = read_stylechange_record_data($this->buf, $this->fsBits, $this->lsBits, $offset, $this->tagID);
		}

		$this->stateNewStyles = $scr["stateNewStyles"];
		$this->stateLineStyle = $scr["stateLineStyle"];
		$this->stateFillStyle1 = $scr["stateFillStyle1"];
		$this->stateFillStyle0 = $scr["stateFillStyle0"];
		$this->stateLineStyle = $scr["stateLineStyle"];

		if($scr["stateMoveTo"]){
			$this->stateMoveTo = 1;
			$this->nBitsMoveTo = $scr["nBitsMoveTo"];
			$this->moveX = $scr["moveX"];
			$this->movey = $scr["moveY"];
		}

		if(isset($scr["fsBits"])) $this->fsBits = $scr["fsBits"];
		if(isset($scr["lsBits"])) $this->lsBits = $scr["lsBits"];
		if($scr["stateFillStyle1"])	$this->fillStyle1 = $scr["fillStyle1"];
		if($scr["stateFillStyle0"])	$this->fillStyle0 = $scr["fillStyle0"];
		if($scr["stateLineStyle"]) $this->lineStyle = $scr["lineStyle"];

		if($scr["stateNewStyles"]){

			$this->fsaData = $scr["fillStyleArray"];
			$this->lsaData = $scr["lineStyleArray"];
			$this->nFillBits = $scr["nFillBits"];
			$this->nLineBits = $scr["nLineBits"];
		}

		$this->binStr = $scr["binStr"];
	}

	// update tagID fsaData, lsaData, if present
	function update_tagID($tagID){

		if($this->tagID == $tagID) return;

		$this->tagID = $tagID;

		if($this->stateNewStyles){

			foreach($this->fsaData as $fs) $fs->update_tagID($tagID);
			foreach($this->lsaData as $ls) $ls->update_tagID($tagID);
		}
	}

	// returns a "first part" of stylechange record
	// even if contains statenewStyles
	// does not include padding bits nor fill or line style array
	function get_binStr_before_pad(){

		$scr = array();
		$scr = $this->fetch_assoc();
		$binStr = write_stylechange_record_data($scr, false);

		return $binStr;
	}

	// set moveTo options
	function update_moveTo($mX, $mY, $inTwips = false){
		$this->stateMoveTo = 1;
		$this->moveX = $inTwips ? $mX : nmb2twip($mX);
		$this->moveY = $inTwips ? $mY : nmb2twip($mY);
		$this->nBitsMoveTo = find_minBits(array($this->moveX, $this->moveY));
	}

	// set "leftFill" options
	function update_leftFill($lfsIndex, $fsLength){
		$this->stateFillStyle0 = 1;
		$this->fsBits = boundBits($fsLength);
		$this->fillStyle0 = $lfsIndex;
	}

	// set "rightFill" options
	function update_rightFill($rfsIndex, $fsLength){
		$this->stateFillStyle1 = 1;
		$this->fsBits = boundBits($fsLength);
		$this->fillStyle1 = $rfsIndex;
	}

	// set "line" options
	function update_lineStyle($lsIndex, $lsLength = 0){
		$this->stateLineStyle = 1;
		$this->lsBits = 0;
		if($lsLength) $this->lsBits = boundBits($lsLength);
		$this->lineStyle = $lsIndex;
	}

	// update binStr from a given bit offset
	// it may be useful when refreshing data
	// from preceeding shape record and when stylechange
	// has stateNewStyle
	function update_binStr_from_offset($offset){
		$scr = array();
		$scr = $this->fetch_assoc();
		return $this->set_stylechange_record_data($scr, $offset);
	}

	// returns an associative array width class members
	// as keys
	function fetch_assoc(){

		$scr = array();
		$scr["stateNewStyles"] = $this->stateNewStyles;
		$scr["stateLineStyle"] = $this->stateLineStyle;
		$scr["stateFillStyle1"] = $this->stateFillStyle1;
		$scr["stateFillStyle0"] = $this->stateFillStyle0;
		$scr["stateLineStyle"] = $this->stateLineStyle;
		$scr["stateMoveTo"] = $this->stateMoveTo;

		if($this->stateMoveTo){
			$scr["nBitsMoveTo"] = $this->nBitsMoveTo;
			$scr["moveX"] = $this->moveX;
			$scr["moveY"] = $this->moveY;
		}

		$scr["fsBits"] = $this->fsBits;
		$scr["lsBits"] = $this->lsBits;

		if($this->stateFillStyle1) $scr["fillStyle1"] = $this->fillStyle1;
		if($this->stateFillStyle0) $scr["fillStyle0"] = $this->fillStyle0;
		if($this->stateLineStyle) $scr["lineStyle"] = $this->lineStyle;

		if($this->stateNewStyles){
			$scr["fillStyleArray"] = $this->fsaData;
			$scr["lineStyleArray"] = $this->lsaData;
			$scr["nFillBits"] = $this->nFillBits;
			$scr["nLineBits"] = $this->nLineBits;
		}

		$scr["binStr"] = $this->binStr;

		return $scr;
	}

	// returns binStr data from its members
	// alias of set_stylechange_record_data
	function update_binStr(){ return $this->set_stylechange_record_data(); }

	function get_bitLength(){ return strlen($this->binStr); }
}

/**
 * STRAIGHTEDGERECORD
 */
class MKStraightEdgeRecord {

	var $buf;
	var $binStr;
	var $typeFlag = 1;		// UB[1]
	var $straightFlag = 1;	// UB[1]
	var $nBits = 0;					// UB[4] + 2
	var $generalLineFlag = 0;		// UB[1]
	var $deltaX = 0;
	var $deltaY = 0;
	var $verticalFlag = 0;			// UB[1] vert/hor line

	// the constructor
	function MKStraightEdgeRecord($buf = "", $offset = 0){

		if(strlen($buf)){
			$this->buf = $buf;
			$this->get_straight_edgerecord_data($offset);
		}
	}

	function set_binStr($binStr) { $this->binStr = $binStr; }
	function get_binStr() { return $this->binStr; }

	//
	function set_straight_edgerecord_data($ser = array()){

		if(count($ser)){
			$this->get_straight_edgerecord_data($ser);
		}
		else{
			$ser = array();
			$ser = $this->fetch_assoc();
		}

		$this->binStr = write_straight_edgerecord_binStr($ser);

		return $this->binStr;
	}

	function get_straight_edgerecord_data($offset, $srd = array()){

		if(!count($srd)){
			$srd = array();
			$srd = read_straight_edgerecord_data($this->buf, $offset);
		}

		$this->nBits = $srd["nBits"];
		$this->generalLineFlag = $srd["generalLineFlag"];
		$this->verticalFlag = $srd["verticalFlag"];

		if($srd["generalLineFlag"]){
			$this->deltaX = $srd["deltaX"];
			$this->deltaY = $srd["deltaY"];
		}
		else if($srd["verticalFlag"]){
			$this->deltaY = $srd["deltaY"];
		}
		else $this->deltaX = $srd["deltaX"];

		$this->binStr = $srd["binStr"];
	}

	// update deltaX and nBits
	function update_deltaX($x, $inTwips = false){

		$this->deltaX = $inTwips ? $x : nmb2twip($x);
		$tmpNBits = find_minBits($this->deltaX) - 2;

		if($tmpNBits > $this->nBits) $this->nBits = $tmpNBits;
	}

	// update deltaY and nBits
	function update_deltaY($y, $inTwips = false){

		$this->deltaY = $inTwips ? $y : nmb2twip($y);
		$tmpNBits = find_minBits($this->deltaY) - 2;

		if($tmpNBits > $this->nBits) $this->nBits = $tmpNBits;
	}

	// set flags
	// general line or orthogonal
	// vertical/horizontal line
	function update_flags(){
		if($this->deltaX && $this->deltaY) $this->generalLineFlag = 1;
		else if($this->deltaY) $this->verticalFlag = 1;
	}

	// returns an associative array width class members
	// as keys
	function fetch_assoc(){

		$srd = array();
		$srd["nBits"] = $this->nBits;
		$srd["generalLineFlag"] = $this->generalLineFlag;
		$srd["verticalFlag"] = $this->verticalFlag;
		$srd["deltaX"] = $this->deltaX;
		$srd["deltaY"] = $this->deltaY;
		$srd["binStr"] = $this->binStr;

		return $srd;
	}

	// returns binStr data from its members
	// alias of set_straight_edgerecord_data
	function update_binStr(){ return $this->set_straight_edgerecord_data(); }

	// return STRAIGHTEDGERECORD bit Length
	function get_bitLength(){ return strlen($this->binStr); }
}

/**
 * CURVEDGERECORD
 */
class MKCurvedEdgeRecord {

	var $buf;
	var $binStr = "";
	var $typeFlag = 1;		// UB[1]
	var $straightFlag = 0;	// UB[1]
	var $nBits = 0;					// UB[4] + 2
	var $cx;					// control Delta X
	var $cy;					// control Delta Y
	var $ax;					// anchor Delta X
	var $ay;					// anchor Delta Y

	// the constructor
	function MKCurvedEdgeRecord($buf = "", $offset = 0){

		if(strlen($buf)){
			$this->buf = $buf;
			$this->get_curved_edgerecord_data();
		}
	}


	function set_binStr($binStr) { $this->binStr = $binStr; }
	function get_binStr() { return $this->binStr; }

	function set_curved_edgerecord_data($ced = array()){

		if(count($ced)){
			$this->get_curved_edgerecord_data($ced);
		}
		else{
			$ced = array();
			$ced = $this->fetch_assoc();
		}

		$this->binStr = write_curved_edgerecord_binStr($ced);

		return $this->binStr;
	}

	function get_curved_edgerecord_data($offset, $ced = array()){

		if(!count($ced)){
			$ced = array();
			$ced = read_curved_edgerecord_data($this->buf, $offset);
		}

		$this->nBits = $ced["nBits"];
		$this->cx = $ced["controlDeltaX"];
		$this->cy = $ced["controlDeltaY"];
		$this->ax = $ced["anchorDeltaX"];
		$this->ay = $ced["anchorDeltaY"];
		$this->binStr = $ced["binStr"];
	}

	// returns an associative array width class members
	// as keys
	function fetch_assoc(){

		$ced = array();
		$ced["controlDeltaX"] = $this->cx;
		$ced["controlDeltaY"] = $this->cy;
		$ced["anchorDeltaX"] = $this->ax;
		$ced["anchorDeltaY"] = $this->ay;
		$ced["binStr"] = $this->binStr;

		return $ced;
	}

	// update class members
	function update_data($cX, $cY, $aX, $aY, $inTwips = false){

		if(!$inTwips){
			$this->cx = nmb2twip($cX);
			$this->cy = nmb2twip($cY);
			$this->ax = nmb2twip($aX);
			$this->ay = nmb2twip($aY);
		}
		else {

			$this->cx = $cX;
			$this->cy = $cY;
			$this->ax = $aX;
			$this->ay = $aY;
		}

		$this->nBits = find_minBits(array($this->cx, $this->cy,
										  $this->ax, $this->ay));
	}

	// returns binStr data from its members
	// alias of set_curved_edgerecord_data
	function update_binStr(){ return $this->set_curved_edgerecord_data(); }

	// return CURVEDEDGERECORD bit Length
	function get_bitLength(){ return strlen($this->binStr); }
}

/**
 * ENDSHAPERECORD
 * this is a sort of static class
 */
class MKEndShapeRecord {

	var $binStr = "000000";

	// the constructor
	function MKendshape_record(){
		$this->binStr = "000000";
		return $this->binStr;
	}

	//
	function get_binStr() { return $this->binStr; }

	// this method is pretty useless :)
	function get_bitLength(){ return 6; }
}

// ------------------- end of mk classes structures
?>
